Skip to content

fix(shader): reuse folded bitcasts and NaN tests within blocks - #11564

Merged
oneandonlydean merged 2 commits into
boykopovar:mainfrom
JJenkx:fix/shader-folded-bitcast-nan-reuse
Oct 28, 2026
Merged

oneandonlydean merged 2 commits into
boykopovar:mainfrom
JJenkx:fix/shader-folded-bitcast-nan-reuse

Conversation

@JJenkx

@JJenkx JJenkx commented Oct 26, 2026 •

Copy link
Copy Markdown
Contributor

What

Late SPIR-V specialization can fold different operands to the same source after IR common-subexpression elimination. This leaves duplicate bitcasts and NaN predicates in the emitted block. Reuse identical casts and NaN tests within each basic block; keep different input/result types, sibling blocks and directly/group-decorated results separate.

This independently fixes the late-fold redundancy behind the existing agc_redundant_value_elimination failure seen with #4287 and ANYPS5_ENABLE_SPIRV_TOOLS=OFF. It does not incorporate or depend on #4287. No title FPS improvement is claimed.

Overlap tool found no confirmed overlaps. Its uncompared #8004 changes shared opcode/result/operand helpers, not this folding logic; reviewed manually. #7677 handles round-trip casts in a different pass.

Tested

Independent SPIRV-Tools-enabled validation by jotitadev: exact head 09c36584f merged onto main 8df2c3ed0, Ubuntu 26.04 / GCC 15.2 Release, ANYPS5_ENABLE_SPIRV_TOOLS=ON, lavapipe Mesa 26.0.3. The test-only red arm fails agc_spirv_module in all 3 runs; the full PR passes the module, optimizer (default/full/none), redundant-value-elimination and SPIR-V 1.3 image checks in all 3 runs. Optimizer bitcast/copy/constant counts remain unchanged after specialization. The 1,053-test suite gives 1,051/1,053 on both main and the PR: the two failures are unchanged baseline failures (agc_driver_multisample_retirement, agc_driver_guest_memory_lifetime_converted). These are reviewer-run results and identify their exact tested source; they are not a title-performance claim.

Linux Release, GCC 16.2.1; base e0b13f2990f31a949a6da21d81a7789ebdfeaa26, change 09c36584fc458d0d1dc60f27f155947e06cfb27e. Optional SPIRV-Tools optimizer disabled throughout these focused tests.

  • The new module regression suite fails five checks on main plus tests, and passes with the fix. Different source/type/branch and direct/group decoration safeguards pass. Both agc_spirv_module and agc_redundant_value_elimination pass on the change.
  • Same seven Vulkan execution tests on main and change: f32 arithmetic NaNs, NaN quieting, f32 min/max NaNs, FMA rounding, legacy FMA, f32 denormal flush and f64 FMA subnormals. Each arm passes 7/7 with seven actual Khronos validation-layer insertions, zero VUID errors, zero skips and no missing-layer warnings. ANYPS5_REQUIRE_VULKAN=1, ANYPS5_NO_SHADER_CACHE=1, VK_INSTANCE_LAYERS=VK_LAYER_KHRONOS_validation; Mesa shader cache also disabled.
  • An additional private integration pair with unchanged perf(shader): pick the f32 NaN result with one quiet and one fallback select #4287 passes the same 7/7 GPU tests in each arm. Its CPU control reproduces exactly two repeated instructions in a 1490-word shader; adding this fix clears that failure. Across all four arms: 28 GPU test executions, 28 actual validation-layer insertions, zero VUID errors.
  • All 15 generated unit-test SPIR-V modules pass standalone spirv-val --target-env vulkan1.3, covering block dominance, types and decorations.
  • Windows-target CPU PE fixtures under local Linux Wine 11.19, MinGW GCC 16.2 UCRT: reproduced the original perf(shader): pick the f32 NaN result with one quiet and one fallback select #4287 two-instruction failure with the optimizer OFF, then both redundancy and module fixtures pass with these exact changed source files. This is not a native Windows CI result; the original Winlibs 15.2 CI configuration remains unverified. The Windows machine was not used.
  • Conventions check passes. No game launch or title timing is claimed by this PR.

Evidence: source pins, paired verbose logs, regression outputs, generated toy modules and reproduction scripts. SHA256: 1df80e10d1252a976cecf493d3c0168610bf2e7094cf30e29bc0ada78f7bad15. Archive publicly downloaded and hash-verified; no game assets or game shader captures included.

Checklist

  • Based on current main; no other open PR implements the same functions
  • One topic per PR; follow-ups go in a new PR
  • No comments in code except technical debt
  • Unimplemented paths throw (NotImplemented_nid_no_patch); silent stubs are listed under Silent stubs in their library's technical debt file
  • No notes, investigation .md files or images added to the repository (attach them to this PR)
  • New third-party code is a submodule built from source
  • Generic behaviour, not specific to one title
  • Depends on: none
  • Fixes: none
  • Unblocks: none
  • GPUs tested: AMD Radeon RX 6650 XT (RADV NAVI23), Mesa 26.2.4-arch1.1, Linux 7.2.9-arch1-1
  • AI-assisted: yes

@github-actions

github-actions Bot commented Oct 26, 2026 •

Copy link
Copy Markdown

Other open pull requests touch the same code. Merging one will break the other, or both implement the same thing. If this one builds on them, list them in Depends on.

Pull request Conflicting files (lines on main) Same exports Same new files
PR 10803 core/libs/prx/libSceAgcDriver/tests/SpirvModule.cpp L476-477

Some comparisons could not be completed because a pull request conflicts with the base branch. Review these shared files manually before concluding that there is no overlap.

Pull request Shared files not compared
PR 7922 core/shader/recompiler/SpirvBackend/src/SpirvSpecialization.cpp
PR 8004 core/shader/recompiler/SpirvBackend/src/SpirvSpecialization.cpp

@oneandonlydean oneandonlydean self-assigned this Oct 26, 2026

@MarcZX10 MarcZX10 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed exact head 09c36584fc458d0d1dc60f27f155947e06cfb27e; no actionable findings. The reuse maps are cleared at function and label boundaries, key casts by result type and source ID, key NaN tests by opcode/type/operand, and avoid reusing decorated results. The added SPIR-V module cases cover same/different values, types, sibling blocks and direct/group decorations. The detailed paired GPU validation report is scoped correctly and makes no title-performance claim; hosted eligibility, conventions, intake, labels and report checks pass, while the build/relinker/product jobs are skipped by path policy. I did not rerun the Vulkan tests locally.

@JJenkx

JJenkx commented Oct 26, 2026

Copy link
Copy Markdown
Contributor Author

Real-title follow-up on current main 74acfad: PPSA06652's house view now reaches a stable measurement scene with the HTTP1/CA prerequisites. Existing per-draw GPU profiling attributes about7.8ms/present to a fragment program used by3draws. A targeted capture produces a small replay request: the registration/intermediate module, not final executed GPU code, goes from83,148 to82,884SPIR-V words and1,138 to1,070casts with this PR;68same-block duplicates removed. Both outputs pass spirv-val --target-env vulkan1.3. Raw game shader/request remains private.

Matched current-base/change shader tests pass7/7 per arm, real Khronos insertion7+7, no VUIDs/skips. The CPU module regression test and four additional candidate graphics checks pass. Private base 2215b13cf559 includes main74acfad + #11611's redirect/SSL/HTTP1 stack + #11145CA + same #6782/#6913 prerequisites; private change 27b64d68cfb1 adds only this PR's test/fix commits. No public branch was rebased or rewritten.

The same-machine/same-scene native pair uses identical saves/controller/renderer settings and complete200:30traces, normal recipe cache, no diagnostic profiler or debugger:

Metric Base2215 Change27b64
Presents 445 445
Frame p50,ms 67.401 67.122
Frame p99,ms 75.159 76.614
GPU busy per present,ms 46.344 46.366

No demonstrated meaningful FPS improvement from this one pair; approximately14.8FPS at3840x2160 in both. Both clean revision/no dropped trace events, readable matching Welcome prompt over house, normal close240s/exit0, all saves/config/kernel64 restored and no remaining process. Native validation enabled, zero VUIDs. Tutorial gameplay/audio remain unverified.

Paired official traces, summary lines, GPU test reports and aggregate shader statistics (32.6MB), SHA256 b9131c28aa8036c86666a9233d352bfcab437af6ee2a0f98589897992d7745e5. The packet contains timing traces; no game assets, shader data, binaries, screenshots or RGP capture.

@github-actions github-actions Bot added shader core/shader agc libSceAgc* labels Oct 27, 2026

@jotitadev jotitadev left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ran this with ANYPS5_ENABLE_SPIRV_TOOLS=ON, the configuration the description leaves out and the CI build skipped. Linux, i7-8700, Ubuntu 26.04, GCC 15.2, Release. Head 09c36584f merged with --no-ff onto main 8df2c3ed0. lavapipe (Mesa 26.0.3).

  • Red: main + only the SpirvModule.cpp changes fails agc_spirv_module 3/3 with "5 check(s) failed", as in the description.
  • Green: with the PR, agc_spirv_module, agc_spirv_optimizer (+ _full, _none), agc_redundant_value_elimination and agc_spirv13_image pass 3/3. agc_spirv_optimizer also checks the bitcast, copy and constant counts after specialization, and they are unchanged.
  • Suite: ^agc plus spirv_validator_exit with SPIRV-Tools ON (1053 tests, display/flip/lds_atomics64 excluded) gives 1051/1053 both on main and with this PR. The two failures (agc_driver_multisample_retirement, agc_driver_guest_memory_lifetime_converted) are the same on main.

GPUs tested: lavapipe (Mesa 26.0.3).

Written with AI assistance (Claude Code). I ran the builds and tests above on my machine.

@JJenkx

JJenkx commented Oct 27, 2026

Copy link
Copy Markdown
Contributor Author

Title data on RADV, for the GPU-time side of this change. The result is neutral.

Builds. Dusk Diver 2 (PPSA06565) title menu, RX 6650 XT, RADV Mesa 26.2.4, Linux 7.2.9.

Each arm had its own warmed shader cache, and the runs alternated, 90 s each, with APS5_PROFILE_GPU=1 APS5_PROFILE_GPU_DRAWS=1. All four measured runs end on the same System settings screen.

Run Build FPS (median after 45 s) GPU batch time per present draws per present
warm-up base 13.10 64.5 ms 35.5 ms
warm-up + this PR 13.01 64.5 ms 35.8 ms
1 base 11.39 70.4 ms 41.4 ms
2 + this PR 12.00 68.9 ms 39.7 ms
3 base 11.75 69.4 ms 40.1 ms
4 + this PR 9.91 75.5 ms 44.0 ms

There is no consistent difference. The spread between runs of the same build (about ±5 %) is larger than any effect. This title's heaviest shaders are dominated by the NaN tests this PR reuses: SelectU32/BitwiseAnd32/UGreaterThan32 outnumber the arithmetic about ten to one in the IR. So RADV's compiler probably already removes the within-block repeats on its own.

This says nothing about the PR's purpose of fixing #4287's optimizer-off failure, nor about other drivers.

AI-assisted: yes (Claude Code).

@oneandonlydean
oneandonlydean merged commit e6d04bf into boykopovar:main Oct 28, 2026
15 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

agc libSceAgc* shader core/shader

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants